package lucava.metamodel.dot.visitor;

import java.util.Iterator;

import lucava.metamodel.dot.DotAssociation;
import lucava.metamodel.dot.DotGraph;
import lucava.metamodel.dot.DotNode;
import lucava.metamodel.dot.DotProperty;
import lucava.metamodel.dot.DotSubgraph;
import lucava.util.FileBuffer;

/**
 * Génère le fichier de description d'un graphe DOT depuis un MetaModel DOT.
 */
public class DotFileGenerator implements IDotMetaModelVisitor {

	/**
	 * Fichier généré.
	 */
	protected FileBuffer mFileBuffer;

	protected String pathToFile = "";

	public DotFileGenerator(String aPathToFile) {
		this.pathToFile = aPathToFile;
	}

	public DotFileGenerator() {
		this("");
	}

	/**
	 * Génère le contenu d'un graphe.
	 * 
	 * @param aDotGraph
	 *            , Graphe DOT dont le contenu est à générer.
	 */
	protected void generateGraphContent(DotGraph aDotGraph) {

		this.mFileBuffer.increaseTab();

		// Génération de tous les sous graphs
		Iterator<DotSubgraph> subgraphIt = aDotGraph.getSubGraphs().iterator();

		while (subgraphIt.hasNext()) {
			subgraphIt.next().accept(this);
		}

		// S'il y a des propriétés générales sur les noeuds
		if (!aDotGraph.getNodeProperties().isEmpty()) {
			this.mFileBuffer.printWithTab("node [");

			// Génération de toutes les propriétés des noeuds
			Iterator<DotProperty> nodePropertyIt = aDotGraph
					.getNodeProperties().iterator();
			while (nodePropertyIt.hasNext()) {
				this.mFileBuffer
						.print(nodePropertyIt.next().getPropertyValue());

				if (nodePropertyIt.hasNext()) {
					this.mFileBuffer.print(",");
				}
			}
			this.mFileBuffer.printLn("];");
		}

		// Génération de toutes les propriétés du graph
		Iterator<DotProperty> graphPropertyIt = aDotGraph.getGraphProperties()
				.iterator();
		while (graphPropertyIt.hasNext()) {
			this.mFileBuffer.printLnWithTab(graphPropertyIt.next()
					.getPropertyValue() + ";");
		}

		// Génération de toutes les associations du graph
		Iterator<DotAssociation> graphAssociationIt = aDotGraph
				.getAssociations().iterator();
		while (graphAssociationIt.hasNext()) {
			graphAssociationIt.next().accept(this);
		}

		// Génération de toutes les propriétés du graph
		Iterator<DotNode> nodeIt = aDotGraph.getNodes().iterator();
		while (nodeIt.hasNext()) {
			// this.mCurrentWriter.printWithTab("");
			nodeIt.next().accept(this);
		}

		this.mFileBuffer.decreaseTab();
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.dot.visitor.IDotMetaModelVisitor#visitDotSubgraph(lucava.metamodel.dot.DotSubgraph)
	 */
	@Override
	public void visitDotSubgraph(DotSubgraph aDotSubgraph) {

		this.mFileBuffer.printLnWithTab("subgraph " + aDotSubgraph.getName()
				+ "{");

		this.generateGraphContent(aDotSubgraph);

		this.mFileBuffer.printLnWithTab("}");

	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.dot.visitor.IDotMetaModelVisitor#visitDotNode(lucava.metamodel.dot.DotNode)
	 */
	@Override
	public void visitDotNode(DotNode aDotNode) {

		this.mFileBuffer.printWithTab(aDotNode.getName());

		// S'il y a des propriétés sur les noeuds
		if (!aDotNode.getNodeProperties().isEmpty()) {
			// Ajout du label du noeud

			this.mFileBuffer.print(" [");

			// Génération de toutes les propriétés du noeud
			Iterator<DotProperty> nodePropertyIt = aDotNode.getNodeProperties()
					.iterator();
			while (nodePropertyIt.hasNext()) {
				this.mFileBuffer
						.print(nodePropertyIt.next().getPropertyValue());

				if (nodePropertyIt.hasNext()) {
					this.mFileBuffer.print(",");
				}
			}
			this.mFileBuffer.print("]");
		}

		this.mFileBuffer.printLn(";");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.dot.visitor.IDotMetaModelVisitor#visitDotGraph(lucava.metamodel.dot.DotGraph)
	 */
	@Override
	public void visitDotGraph(DotGraph aDotGraph) {

		// Création du nom du fichier dont le contenu est généré
		String fileName = aDotGraph.getName() + ".dot";

		// Ouverture du flux d'écriture sur le fichier
		this.mFileBuffer = new FileBuffer(this.pathToFile + fileName);

		this.mFileBuffer
				.printLnWithTab("digraph " + aDotGraph.getName() + " {");

		this.mFileBuffer.increaseTab();

		// Génération du contenu du graphe
		this.generateGraphContent(aDotGraph);

		this.mFileBuffer.decreaseTab();
		this.mFileBuffer.printLnWithTab("}");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.dot.visitor.IDotMetaModelVisitor#visitDotAssociation(lucava.metamodel.dot.DotAssociation)
	 */
	@Override
	public void visitDotAssociation(DotAssociation aDotAssociation) {
		this.mFileBuffer.printWithTab(aDotAssociation.getFromNode().getName());
		this.mFileBuffer.print(" -> ");
		this.mFileBuffer.print(aDotAssociation.getToNode().getName());

		if (!aDotAssociation.getProperties().isEmpty()) {
			this.mFileBuffer.print(" [");

			// Génération de toutes les propriétés de l'association
			Iterator<DotProperty> associationPropertyIt = aDotAssociation
					.getProperties().iterator();
			while (associationPropertyIt.hasNext()) {
				this.mFileBuffer.print(associationPropertyIt.next()
						.getPropertyValue());

				if (associationPropertyIt.hasNext()) {
					this.mFileBuffer.print(",");
				}
			}

			this.mFileBuffer.print("]");
		}

		this.mFileBuffer.printLn(";");

	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.dot.visitor.IDotMetaModelVisitor#visitDotProperty(lucava.metamodel.dot.DotProperty)
	 */
	@Override
	public void visitDotProperty(DotProperty aDotProperty) {
		// Do nothing...
	}

	/**
	 * Retourne le fichier généré.
	 */
	public FileBuffer getGeneratedFile() {
		return mFileBuffer;
	}
}
